﻿#include "H264OrH265FilePlayByEncapsulationWindow.h"

extern "C"
{
#include "libavcodec/avcodec.h"
#include "libavutil/avutil.h"
}
#include <QFileDialog>
#include <QMessageBox>
#include <QSize>


H264OrH265FilePlayByEncapsulationWindow::H264OrH265FilePlayByEncapsulationWindow(QWidget* parent)
	: QWidget(parent)
{
	ui.setupUi(this);
	this->setWindowTitle("H264或H265文件播放器(封装)");
	setAttribute(Qt::WA_DeleteOnClose, true);  // 关闭时自动销毁窗口
	codecId = AV_CODEC_ID_H264;
}

void H264OrH265FilePlayByEncapsulationWindow::onVideoFormatChanged(int pos)
{
	switch (pos)
	{
	case 0:
		codecId = AV_CODEC_ID_H264;
		break;
	case 1:
		codecId = AV_CODEC_ID_HEVC;
		break;
	}
}

void H264OrH265FilePlayByEncapsulationWindow::onCodecFormatChanged(int pos)
{
	switch (pos)
	{
	case 0:
		isHwCodec = false;
		break;
	case 1:
		isHwCodec = true;
		break;
	}
}

void H264OrH265FilePlayByEncapsulationWindow::onSelectedFile()
{
	QString filter = "所有文件(*.*)"; //文件过滤器
	QString filePath = QFileDialog::getOpenFileName(this, "选择文件输出目录", "", filter);
	this->fileName = filePath;
	this->ui.outputPathEdit->setText(this->fileName);
}

void H264OrH265FilePlayByEncapsulationWindow::onStartPlayVideo()
{

	isRunning = true;
	videoWidth = ui.editWidth->text().toInt();
	videoHeight = ui.editHeight->text().toInt();
	if (this->fileName.isEmpty() || videoWidth <= 0 || videoHeight <= 0) {
		QMessageBox::information(this, "温馨提示", "参数不对请检查参数");
		return;
	}
	ui.labelVideo->move(0, 0);
	ui.labelVideo->resize(QSize(videoWidth, videoHeight));
	resize(QSize(videoWidth, videoHeight));

	//创建一个线程解码
	renderView = IVideoRenderView::Create();
	//IVideoRenderView::YUV420P
	if (isHwCodec)
	{
		renderView->Init(videoWidth, videoHeight, IVideoRenderView::NV12, (void*)ui.labelVideo->winId());
	}
	else {
		renderView->Init(videoWidth, videoHeight, IVideoRenderView::YUV420P, (void*)ui.labelVideo->winId());
	}
	reader = BinaryFileReader::Create();
	reader->filename = ui.outputPathEdit->text().toStdString();//文件名
	reader->width = videoWidth;
	reader->height = videoHeight;
	reader->Init();
	renderThread = std::make_unique<std::thread>(&H264OrH265FilePlayByEncapsulationWindow::Draw, this);

}

void H264OrH265FilePlayByEncapsulationWindow::Draw()
{

	YDecoder* decoder = new YDecoder();
	AVCodecContext* context = decoder->Create(codecId, false);
	context->width = videoWidth;
	context->height = videoHeight;
	decoder->setContext(context);
	if (isHwCodec) {
		decoder->InitHW(AV_HWDEVICE_TYPE_DXVA2);
	}
	decoder->Open();
	


	//创建文件分割上下文
	auto parser = av_parser_init(codecId);
	//申请packet空间
	auto pkt = av_packet_alloc();
	//申请frame空间
	/*auto *frame = decoder->CreateFrame();*/
	auto* frame = av_frame_alloc();
	while (isRunning)
	{
		bool flag = reader->Read((char*)inbuf, sizeof(inbuf));
		if (!flag)
		{
			qDebug() << "文件读取结束";
			break;
		}
		int data_size = reader->getBytesCount();
		if (data_size <= 0) {
			qDebug() << "没有读到数据退出循环";
			break;
		}
		auto data = inbuf;
		while (data_size > 0) //一次有多帧数据
		{
			//通过0001 截断输出到AVPacket 返回帧大小
			int ret = av_parser_parse2(parser, context,
				&pkt->data, &pkt->size, //输出
				data, data_size,        //输入
				AV_NOPTS_VALUE, AV_NOPTS_VALUE, 0
			);
			data += ret;
			data_size -= ret; //已处理
			if (pkt->size)
			{
				if (!decoder->Send(pkt))break;
				while (decoder->Receive(frame))
				{
					qDebug() << frame->format << "";
					renderView->DrawFrame(frame);
					std::this_thread::sleep_for(std::chrono::milliseconds(1000 / 25));
				}
				
			}
		}

	}
	///取出缓存数据
	auto frames = decoder->End();
	for (auto f : frames)
	{
		renderView->DrawFrame(f);
		av_frame_free(&f);
	}

	av_parser_close(parser);
	avcodec_free_context(&context);
	av_frame_free(&frame);
	av_packet_free(&pkt);
	if (decoder)
	{
		delete decoder;
		decoder = nullptr;
	}
}

void H264OrH265FilePlayByEncapsulationWindow::Release()
{
	if (isRunning)
	{
		isRunning = false;
		renderThread->join();
		if (renderView) {
			renderView->Close();
			renderView = nullptr;
		}
		if (reader)
		{
			reader->Close();
			reader = nullptr;
		}
	}

}

void H264OrH265FilePlayByEncapsulationWindow::closeEvent(QCloseEvent* event) {
	event->accept();  // 接受关闭事件（允许关闭）
	Release();
	qDebug() << "窗口已关闭";
}

H264OrH265FilePlayByEncapsulationWindow::~H264OrH265FilePlayByEncapsulationWindow()
{
}
